//
// Created by kidon on 20-9-4.
//

#include <jni.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>

#include "video_encoder.h"
#include "error_code.h"
#include "logger.h"

#define PACKAGE_NAME com_taichuan_media
#define CLASS_NAME TcMedia

#define FUNC3(PKG3, CLASS3, NAME3) Java_##PKG3##_##CLASS3##_##NAME3
#define FUNC2(PKG2, CLASS2, NAME2) FUNC3(PKG2, CLASS2, NAME2)
#define FUNC(NAME) FUNC2(PACKAGE_NAME, CLASS_NAME, NAME)

#define TAG "jni_encoder"

/**
 * 准备视频编码器
 *
 * @param env
 * @param clazz
 * @param src_width
 * @param src_height
 * @param src_pix_fmt_type
 * @param codec_type
 * @param fps
 * @param bit_rate
 * @return 编码器数据结构体地址
 */
JNIEXPORT jlong JNICALL FUNC(prepareVideoEncoder)(JNIEnv *env, jclass clazz,
                                                  jint codec_type,
                                                  jint src_width, jint src_height,
                                                  jint src_pix_fmt_type,
                                                  jint dest_width, jint dest_height,
                                                  jint crop_type,
                                                  jint fps,
                                                  jint bit_rate
) {
    jlong result;
    int ret;
    TcVideoEncoder *encoder;

    ret = video_encoder_prepare(&encoder,
                                (enum CodecType) codec_type,
                                src_width, src_height,
                                (enum PixelFormatType) src_pix_fmt_type,
                                dest_width, dest_height,
                                (enum ImageCropTypes) crop_type,
                                fps,
                                bit_rate);
    if (ret != SUCCESS) {
        result = (jlong) NULL;
    } else {
        result = (jlong) encoder;
    }

    return result;
}

/**
 * 将数据写入编码器
 *
 * @param env
 * @param clazz
 * @param encoder_id
 * @param data
 * @param offset
 * @param len
 * @return
 */
JNIEXPORT jint JNICALL FUNC(writeVideoFrameToEncoder)(JNIEnv *env, jclass clazz,
                                                      jlong encoder_id,
                                                      jbyteArray data,
                                                      jint offset,
                                                      jint len
) {
    TcVideoEncoder *encoder = (TcVideoEncoder *) encoder_id;
    // 获取数据数组
    jbyte *c_data = (*env)->GetByteArrayElements(env, data, 0);

    jint result = video_encoder_write_frame(encoder, (uint8_t *) c_data, offset, len);

    // 释放数据数组
    (*env)->ReleaseByteArrayElements(env, data, c_data, 0);

    return result;
}

/**
 * 从编码器读取数据
 *
 * @param env
 * @param clazz
 * @param encoder_id
 * @param data
 * @param offset
 * @return
 */
JNIEXPORT jint JNICALL FUNC(readVideoFrameFromEncoder)(JNIEnv *env, jclass clazz,
                                                       jlong encoder_id,
                                                       jbyteArray data,
                                                       jint offset
) {
    TcVideoEncoder *encoder = (TcVideoEncoder *) encoder_id;
    // 读取数据
    int len = (*env)->GetArrayLength(env, data);
    jbyte *c_data = (*env)->GetByteArrayElements(env, data, 0);
    jint result = video_encoder_read_frame(encoder, (uint8_t *) c_data, offset);
    if (result > 0) {
        (*env)->SetByteArrayRegion(env, data, 0, len, c_data);
    } else {
        // 释放数据数组
        (*env)->ReleaseByteArrayElements(env, data, c_data, 0);
    }
    return result;
}

/**
 * 排空编码器数据
 *
 * @param env
 * @param clazz
 * @param encoder_id
 * @return
 */
JNIEXPORT jint JNICALL FUNC(flushVideoEncoder)(JNIEnv *env, jclass clazz,
                                               jlong encoder_id
) {
    TcVideoEncoder *encoder = (TcVideoEncoder *) encoder_id;
    jint result = video_encoder_flush(encoder);
    return result;
}

/**
 * 释放编码器
 *
 * @param env
 * @param clazz
 * @param encoder_id
 * @return
 */
JNIEXPORT jint JNICALL FUNC(releaseVideoEncoder)(JNIEnv *env, jclass clazz,
                                                 jlong encoder_id
) {
    TcVideoEncoder *encoder = (TcVideoEncoder *) encoder_id;
    jint result = video_encoder_release(&encoder);
    return result;
}